package org.fox.beetl.ext.spring.security;

import java.util.Collection;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import org.beetl.core.Context;
import org.beetl.core.Tag;
import org.fox.beetl.ext.spring.utils.Utils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.web.context.ServletContextAware;

/**
 * Spring Security &lt;authorize url="..."&gt;标签实现
 *
 * @author Chen Rui
 */
public class AccessUrlIfTag extends Tag implements ApplicationContextAware, ServletContextAware {
	/* ----- ----- ----- ----- IOC属性 ----- ----- ----- ----- */
	/**
	 * Spring应用程序上下文
	 */
	private ApplicationContext applicationContext = null;
	/**
	 * Servlet Context上下文
	 */
	private ServletContext servletContext = null;

	/**
	 * Spring应用程序上下文
	 *
	 * @param applicationContext
	 */
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
	}

	/**
	 * Servlet Context上下文
	 *
	 * @param servletContext
	 */
	@Override
	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;
	}

	/* ----- ----- ----- ----- 其他方法 ----- ----- ----- ----- */
	/**
	 * 标签渲染类
	 */
	@Override
	public void render() {
		// 参数异常
		if ((args.length == 0) || (args[0] == null) || (!(args[0] instanceof String))) {
			throw new IllegalArgumentException("tag需要一个String表示的url");
		}

		String url = (String) args[0];
		String method = "GET";

		if ((args.length > 1) && (args[1] != null) && (args[1] instanceof String)) {
			method = (String) args[1];
		}

		boolean authorized = false;

		String contextPath = servletContext.getContextPath();
		Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
		authorized = getPrivilegeEvaluator(ctx).isAllowed(contextPath, url, method, currentUser);

		// 认证成功，执行标签体
		if (authorized) {
			doBodyRender();
		}
	}

	/**
	 * 从上下文中获取基于Web Url的执行权限评估器实例<br>
	 * 本方法参照Spring Security源码修改
	 *
	 * @param context
	 * @return
	 */
	private WebInvocationPrivilegeEvaluator getPrivilegeEvaluator(Context context) {
		HttpServletRequest request = Utils.getRequest(context);
		if (request != null) {
			WebInvocationPrivilegeEvaluator privEvaluatorFromRequest = (WebInvocationPrivilegeEvaluator) request
					.getAttribute(WebAttributes.WEB_INVOCATION_PRIVILEGE_EVALUATOR_ATTRIBUTE);
			if (privEvaluatorFromRequest != null) {
				return privEvaluatorFromRequest;
			}
		}

		Collection<WebInvocationPrivilegeEvaluator> webInvocationPrivilegeEvaluators = applicationContext
				.getBeansOfType(WebInvocationPrivilegeEvaluator.class).values();
		if (webInvocationPrivilegeEvaluators.isEmpty()) {
			throw new IllegalStateException("Spring上下文中不存在有效的WebInvocationPrivilegeEvaluator实例，请检查Spring Security配置");

		}
		return (WebInvocationPrivilegeEvaluator) webInvocationPrivilegeEvaluators.toArray()[0];
	}
}
